package com.example.backend.config;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.lang.NonNull;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import java.time.Instant;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 安全拦截器
 * 提供请求频率限制、IP黑名单等功能
 */
@Component
public class SecurityInterceptor implements HandlerInterceptor {

    // IP请求记录 - 使用时间窗口记录
    private final ConcurrentHashMap<String, java.util.List<Instant>> ipRequestTimes = new ConcurrentHashMap<>();
    
    /**
     * 定期清理过期数据（每分钟执行一次）
     */
    @Scheduled(fixedRate = 60000) // 每分钟执行一次
    public void cleanupExpiredData() {
        Instant oneHourAgo = Instant.now().minusSeconds(3600);
        ipRequestTimes.forEach((ip, requestTimes) -> {
            requestTimes.removeIf(time -> time.isBefore(oneHourAgo));
            // 如果列表为空，移除该IP的记录
            if (requestTimes.isEmpty()) {
                ipRequestTimes.remove(ip);
            }
        });
    }
    
    // 配置参数 - 优化版（适应SPA应用请求模式）
    // 开发环境使用更宽松的限制，生产环境可以收紧
    private static final int MAX_REQUESTS_PER_MINUTE = 1000; // 每分钟最大请求数（适应SPA应用）
    private static final int MAX_REQUESTS_PER_HOUR = 20000; // 每小时最大请求数
    // TODO: 实现每日请求限制和清理功能
    // private static final int MAX_REQUESTS_PER_DAY = 2000; // 每天最大请求数
    // private static final long CLEANUP_INTERVAL = 300000; // 5分钟清理一次过期记录
    
    @Override
    public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) throws Exception {
        String clientIp = getClientIpAddress(request);
        
        // 检查IP黑名单（这里可以扩展为从数据库或Redis读取）
        if (isIpBlacklisted(clientIp)) {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("{\"success\":false,\"message\":\"IP地址被禁止访问\"}");
            return false;
        }
        
        // 检查可疑请求模式
        if (isSuspiciousRequest(request)) {
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("{\"success\":false,\"message\":\"请求格式异常\"}");
            return false;
        }
        
        // 请求频率限制
        if (!isRequestAllowed(clientIp)) {
            response.setStatus(429); // HTTP 429 Too Many Requests
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("{\"success\":false,\"message\":\"请求过于频繁，请稍后再试\"}");
            return false;
        }
        
        // 记录请求
        recordRequest(clientIp);
        
        return true;
    }
    
    /**
     * 检查是否为可疑请求
     */
    private boolean isSuspiciousRequest(HttpServletRequest request) {
        String userAgent = request.getHeader("User-Agent");
        String uri = request.getRequestURI();
        
        // 检查User-Agent
        if (userAgent == null || userAgent.trim().isEmpty()) {
            return true;
        }
        
        // 检查可疑的User-Agent模式
        String[] suspiciousPatterns = {
            "sqlmap", "nikto", "nmap", "masscan", "zap", "burp",
            "scanner", "bot", "crawler", "spider", "harvester"
        };
        
        String lowerUserAgent = userAgent.toLowerCase();
        for (String pattern : suspiciousPatterns) {
            if (lowerUserAgent.contains(pattern)) {
                return true;
            }
        }
        
        // 检查URI长度
        if (uri.length() > 2000) {
            return true;
        }
        
        // 检查SQL注入模式
        String[] sqlPatterns = {
            "'", "\"", ";", "--", "/*", "*/", "xp_", "sp_",
            "union", "select", "insert", "update", "delete", "drop"
        };
        
        String lowerUri = uri.toLowerCase();
        for (String pattern : sqlPatterns) {
            if (lowerUri.contains(pattern)) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * 获取客户端真实IP地址
     */
    private String getClientIpAddress(HttpServletRequest request) {
        String xForwardedFor = request.getHeader("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) {
            return xForwardedFor.split(",")[0].trim();
        }
        
        String xRealIp = request.getHeader("X-Real-IP");
        if (xRealIp != null && !xRealIp.isEmpty() && !"unknown".equalsIgnoreCase(xRealIp)) {
            return xRealIp;
        }
        
        return request.getRemoteAddr();
    }
    
    /**
     * 检查IP是否在黑名单中
     */
    private boolean isIpBlacklisted(String ip) {
        // 这里可以从数据库或Redis中查询黑名单
        // 示例：检查本地回环地址（仅作演示）
        return false; // 暂时不启用黑名单功能
    }
    
    /**
     * 检查请求是否被允许
     */
    private boolean isRequestAllowed(String ip) {
        Instant now = Instant.now();
        Instant oneMinuteAgo = now.minusSeconds(60);
        Instant oneHourAgo = now.minusSeconds(3600);
        
        // 获取该IP的请求时间列表
        java.util.List<Instant> requestTimes = ipRequestTimes.computeIfAbsent(ip, k -> new java.util.concurrent.CopyOnWriteArrayList<>());
        
        // 清理过期数据（移除1小时前的记录）
        requestTimes.removeIf(time -> time.isBefore(oneHourAgo));
        
        // 检查每分钟请求数
        long minuteCount = requestTimes.stream()
                .filter(time -> time.isAfter(oneMinuteAgo))
                .count();
        if (minuteCount >= MAX_REQUESTS_PER_MINUTE) {
            return false;
        }
        
        // 检查每小时请求数
        long hourCount = requestTimes.size();
        if (hourCount >= MAX_REQUESTS_PER_HOUR) {
            return false;
        }
        
        return true;
    }
    
    /**
     * 记录请求
     */
    private void recordRequest(String ip) {
        Instant now = Instant.now();
        java.util.List<Instant> requestTimes = ipRequestTimes.computeIfAbsent(ip, k -> new java.util.concurrent.CopyOnWriteArrayList<>());
        requestTimes.add(now);
        
        // 定期清理过期数据（保留最近1小时的数据）
        if (requestTimes.size() > MAX_REQUESTS_PER_HOUR) {
            Instant oneHourAgo = now.minusSeconds(3600);
            requestTimes.removeIf(time -> time.isBefore(oneHourAgo));
        }
    }
}
